var moobbcode = new Class({
	Implements: Options,
	options: {
		transformLb: true,
		encodeEmails: true,
		parseSmilies: false,
		smilies: {},
		smiliePath: ""
	},

	initialize: function(text, options) {
		this.text = text;
		this.setOptions(options);
		this.tags = [
		"[u]",
		"[/u]",
		"[i]",
		"[/i]",
		"[b]",
		"[/b]",
		"[s]",
		"[/s]",
		"[indent]",
		"[/indent]",
		"[smallcaps]",
		"[/smallcaps]",
		"[big]",
		"[/big]",
		"[small]",
		"[/small]",
		"[tt]",
		"[/tt]",
		"[sub]",
		"[/sub]",
		"[sup]",
		"[/sup]"
		];

		this.tagsRep = [
		"<span style=\"text-decoration:underline;\">",
		"</span>",
		"<em>",
		"</em>",
		"<strong>",
		"</strong>",
		"<span style=\"text-decoration: line-through;\">",
		"</span>",
		"<blockquote>",
		"</blockquote>",
		'<span style="font-variant: small-caps;">',
		'</span>',
		'<span style="font-size: 120%;">',
		'</span>',
		'<span style="font-size: 70%;">',
		'</span>',
		'<tt>',
		'</tt>',
		'<sub>',
		'</sub>',
		'<sup>',
		'</sup>'
		];

		var smilies = new Hash({
			':), :-)': 'happy.gif',
			':(, :-(': 'sad.gif',
			':P, :-P': 'tongue.gif',
			':O, :-O': 'oh.gif',
			'8), 8-)': 'cool.gif'
		});

		this.smilies = smilies.extend(this.options.smilies);
	},

	setText: function(text) {
		this.text = text;
	},

	toBBCode: function() {
		// normalize line ending
		this.text = this.text.replace(/\r\n/g, "\n");
		this.text = this.text.replace(/\r/g, "\n");

		this.text = this.htmlDecode(this.text);

		this.text = this.unparseQuotes(this.text);

		this.text = this.unparseLists(this.text);

		this.text = this.unparseSmilies(this.text);

		this.text = this.unparseComplexItems(this.text);


		this.text = this.parseSpanItems(this.text, false);

		this.text = this.text.replace(/<br \/>/gi, "\n");
		this.text = this.text.replace(/<br>/gi, "\n");

		return this.text;
	},

	toHtml: function() {
		// normalize line ending
		this.text = this.text.replace(/\r\n/g, "\n");
		this.text = this.text.replace(/\r/g, "\n");

		//first remove html chars...
		this.text = this.htmlEncode(this.text);

		// Parse simple span items
		this.text = this.parseSpanItems(this.text, true);

		//Fix incorrect links
		this.text = this.fixLinks(this.text);

		//parse complex items (urls, emails etc..)
		this.text = this.parseComplexItems(this.text);

		//parse lists (both ul and ol)
		this.text = this.parseLists(this.text);

		//parse quotes
		this.text = this.parseQuotes(this.text);

		//parse smilies
		if(this.options.parseSmilies) {
			this.text = this.parseSmilies(this.text);
		}
		//parse \n
		if(this.options.transformLb) {
			this.text = this.convertLb(this.text);
		}

		return this.text;

	},

	parseQuotes: function(text) {
		text = text.replace(/\[quote\]\s*(?=\S)([^\r]*?\S)\s*\[\/quote\]/gi, function(wholeQuote, quoteContent){
			var quote = "<div class=\"bbcode-quote\"><blockquote>" + quoteContent + "</blockquote></div>";
			return quote;
		}.bind(this));

		text = text.replace(/\[quote\=&quot;([\S]*)&quot;\]\s*(?=\S)([^\r]*?\S)\s*\[\/quote\]/gi, function(wholeQuote, name, content){
			var quote = "<div class=\"bbcode-quote\">" + name + " said: <blockquote>" + content + "</blockquote></div>";
			return quote;
		}.bind(this));
		return text;
	},

	unparseQuotes: function(text) {
		text = text.replace(/<div class\="bbcode-quote"><blockquote>\s*(?=\S)([^\r]*?\S)\s*<\/blockquote><\/div>/gi, "[quote]$1[/quote]");

		text = text.replace(/<div class\="bbcode-quote">([\S]*) said: <blockquote>\s*(?=\S)([^\r]*?\S)\s*<\/blockquote><\/div>/gi, "[quote=\"$1\"]$2[/quote]");
		return text;
	},

	encodeEmail: function(email) {
		var encoded = "";
		for(var i = 0; i<email.length; i++) {
			encoded = encoded + "&#" + email.charCodeAt(i) + ";";
		}

		return encoded;
	},

	decodeEmail: function(email) {
		email = email.replace(/&#([0-9]{1,3});/g, function(wholeMatch, charNum){

			return String.fromCharCode(charNum);
		});
		return email;
	},

	unparseComplexItems: function(text) {

		//unparse emails...
		text = text.replace(/<a href\="mailto: ([^"]+)">([^<]+)<\/a>/gi, function(wholeMatch, email, label) {
			var decodedMail = this.decodeEmail(email);
			return "[email=" + decodedMail + "]" + this.decodeEmail(label) + "[/email]";
		}.bind(this));

		//unparse urls...
		text = text.replace(/<a href="([^"]+)" target="_blank">(.*)<\/a>/gi, "[url=$1]$2[/url]");

		//unparse images...
		text = text.replace(/<img src\="([^"]+)" border\="0" \/>/g, "[img]$1[/img]");



		//unparse fonts...
		text = text.replace(/<span style\="font-family: ([^;]+);">(\s*(?=\S)([^\r]*?\S)\s*)<\/span>/gi, "[font=$1]$2[/font]");
		//and colors...
		text = text.replace(/<span style\="color: ([^;]+);">(\s*(?=\S)([^\r]*?\S)\s*)<\/span>/gi, "[color=$1]$2[/color]");
		//and size
		text = text.replace(/<span style\="font-size: (\d{1,3})(%|px);">(\s*(?=\S)([^\r]*?\S)\s*)<\/span>/gi, function(whole, num, type, content){
			type = type == "px" ? "" : "%";
			return "[size=" + num + type + "]" + content + "[/size]";
		});
		return text;
	},

	parseComplexItems: function(text) {
		//parse code blocks
		text = text.replace(/^(\n*)\[code\](\s*(?=\S)([^\r]*?\S)\s*)\[\/code\](\n*)$/gim, "$1\n<div class=\"bbcode-code\"><code style=\"white-space: pre;\">$2</code><\div>\n$3");

		//parse urls...
		text = text.replace(/\[url\=([^\]]*)\](.*)\[\/url\]/gi, "<a href=\"$1\" target=\"_blank\">$2</a>");
		text = text.replace(/\[url\]([^\]]*)\[\/url\]/gi, "<a href=\"$1\" target=\"_blank\">$1</a>");

		//parse images
		text = text.replace(/\[img\](.*)\[\/img\]/gi, "<img src=\"$1\" border=\"0\" />");

		//parse emails
		text = text.replace(/\[email\=([^\]]*)\]([^\[]*)\[\/email\]/gi, function(wholeMatch, email, label) {
				var encodedEmail = this.options.encodeEmails ? this.encodeEmail(email) : email;
				return "<a href=\"mailto: " + encodedEmail + "\">" + label + "</a>";
		}.bind(this));

		text = text.replace(/\[email\](.*)\[\/email\]/gi, function(wholeMatch, email) {
			var encodedEmail = this.options.encodeEmails ? this.encodeEmail(email) : email;
			return "<a href=\"mailto: " + encodedEmail + "\">" + encodedEmail + "</a>";
		}.bind(this));

		//parse fonts...
		text = text.replace(/\[font\=([^\]]*)\](\s*(?=\S)([^\r]*?\S)\s*)\[\/font\]/gi, "<span style=\"font-family: $1;\">$2</span>");
		//and colors...
		text = text.replace(/\[color\=(\#[0-9A-F]{0,6}|[A-z]+)\](\n*(?=\S)([^\r]*?\S)\n*)\[\/color\]/gi, "<span style=\"color: $1;\">$2</span>");
		//and size...
		text = text.replace(/\[size\=(\d{2,3})(%?)\](\n*(?=\S)([^\r]*?\S)\n*)\[\/size\]/gi, function(whole, size, type, content){
			type = type=="%" ? type : "px";
			return "<span style=\"font-size: " + size + type + ";\">" + content +"</span>";
			});
		return text;
	},

	unparseLists: function(text) {
		text = text.replace(/<ul>(?=\S)([^\r]*?\S)<\/ul>/gi, function(wholeMatch, content){
			wholeMatch = wholeMatch.replace(/<(\/?)ul>/gi, "[$1list]\n");
			wholeMatch = wholeMatch.replace(/<li>/gi, "[*]");
			wholeMatch = wholeMatch.replace(/<\/li>/gi, "\n");
			wholeMatch = wholeMatch.replace(/\n$/, "");

			return wholeMatch;
		});

		text = text.replace(/<ol style\="list-style-type: ([^;]+);">(?=\S)([^\r]*?\S)<\/ol>/gi, function(wholeMatch, type){
			var type = type == "numeric" ? "1" : "a";
			wholeMatch = wholeMatch.replace(/<ol style\="list-style-type: ([^;])+;">/gi, "[list=" + type + "]\n");
			wholeMatch = wholeMatch.replace(/<li>/gi, "[*]");
			wholeMatch = wholeMatch.replace(/<\/li>/gi, "\n");
			wholeMatch = wholeMatch.replace(/<\/ol>/gi, "[/list]");
			return wholeMatch;
		});

		return text;
	},

	parseLists: function(text) {
		if(listMatches = text.match(/\n?\[list\]\s*(?=\S)([^\r]*?\S)\s*\[\/list\]\n?/gi)) {
			listMatches.each(function(aMatch){
				var listContent = aMatch.match(/\[list\]\s*(?=\S)([^\r]*?\S)\s*\[\/list\]/i);
				var items = listContent[1].split("[*]");
				items = items.splice(1, items.length);
				var itemArray = [];
				items.each(function(item){
					item = item.replace("\n", "");
					itemArray.push("<li>" + item + "</li>");
				}.bind(this));
				text = text.replace(aMatch, "<ul>" + itemArray.join("") + "</ul>");
			}.bind(this));
		}

		if(listMatches = text.match(/\[list\=([a1])\]\s*(?=\S)([^\r]*?\S)\s*\[\/list\]/gi)) {
			listMatches.each(function(aMatch){
				var listContent = aMatch.match(/\[list\=([a1])\]\s*(?=\S)([^\r]*?\S)\s*\[\/list\]/i);
				var listType = listContent[1];
				var listItems = listContent[2].split("[*]");
				listItems = listItems.splice(1, listItems.length);
				var itemArray = [];
				listItems.each(function(item){
					item = item.replace("\n", "");
					itemArray.push("<li>" + item + "</li>");
				}.bind(this));
				listType = listType == "a" ? "lower-alpha" : "numeric";
				text = text.replace(aMatch, "<ol style=\"list-style-type: " + listType + ";\">" + itemArray.join("") + "</ol>")
			}.bind(this));
		}

		return text;
	},

	convertLb: function(text) {
		text = text.replace(/\n/g, "<br />");
		//now remove <br> where there shouldn't be any...
		var wrappers = [
			"<div class\\=\"bbcode\\-code\"><code style\\=\"white-space: pre;\">",
			"<\\/code><\\/div>",
			"<ul>",
			"<\\/ul>",
			"<ol>",
			"<\\/ol>"
		];

		for(var i=0; i < wrappers.length; i = i+2) {
			var regExpStr = wrappers[i] + "([\\s]*(?=\\S)([^\\r]*?\\S)[\\s]*)" + wrappers[i+1];
			var regExp = new RegExp(regExpStr, 'gi');
			console.log("here");
			text = text.replace(regExp, function(match){

				return match.replace(/<br \/>/gi, "\n");
			});

		}

		return text;
	},

	htmlDecode: function(text) {
		text = text.replace(/&amp;/g, "&");
		text = text.replace(/&quot;/g, '"');
		text = text.replace(/&#039;/g, "'");
		text = text.replace(/&lt;/g, "<");
		text = text.replace(/&gt;/g, ">");

		return text;
	},

	htmlEncode:function(text) {
		text = text.replace(/&/g, "&amp;");
		text = text.replace(/"/g, "&quot;");
		text = text.replace(/'/g, "&#039;");
		text = text.replace(/</g, "&lt;");
		text = text.replace(/>/g, "&gt;");

		return text;
	},

	parseSpanItems: function(text, tohtml) {
		for(var i=0; i<this.tags.length; i = i+2) {
			var start = tohtml ? "(" + this.tags[i].escapeRegExp() + ")" : "(" + this.tagsRep[i].escapeRegExp() + ")";
			var end = tohtml ? "(" + this.tags[i+1].escapeRegExp() + ")" : "(" + this.tagsRep[i+1].escapeRegExp() + ")";

			var regExpStr = start + "([\\s]*(?=\\S)([^\\r]*?\\S)[\\s]*)" + end;
			var regExp = new RegExp(regExpStr, 'gi');
			var replacementStr = tohtml ? this.tagsRep[i] + "$2" + this.tagsRep[i+1] : this.tags[i] + "$2" + this.tags[i+1];
			text = text.replace(regExp, replacementStr);

		}
		return text;
	},

	unparseSmilies: function(text) {
		this.smilies.each(function(value, key){
			var smilieExp = new RegExp("<img src=\"" + this.options.smiliePath.escapeRegExp() + value.escapeRegExp() + "\" border=\"0\" />", 'gi');
			key = key.replace(/, [ ]?/g, "|");
			keys = key.split("|");
			key = keys[0];
			text = text.replace(smilieExp, key);
		}.bind(this));

		return text;
	},

	parseSmilies: function(text) {
		this.smilies.each(function(value, key){
			key = key.escapeRegExp();
			key = key.replace(/,[ ]?/g, "|");
			var smilieExpStr = "(" + key + ")";
			var smilieExp = new RegExp(smilieExpStr, 'gi');
			text = text.replace(smilieExp, "<img src=\"" + this.options.smiliePath + value + "\" border=\"0\" />");
		}.bind(this));

		return text;
	},

	fixLinks: function(text) {
		text = text.replace(/\[url\](www\..+)\[\/url\]/gi, "[url=http://$1]$1[/url]");
		text = text.replace(/\[url=(www\..+)\](.*)\[\/url\]/gi, "[url=http://$1]$2[/url]");

		text = text.replace(/\[color\=&quot;(\#[0-9A-F]{0,6}|[A-z]+)&quot;\]/gi, "[color=$1]");

		return text;
	}


})